/** \file Hsm_t.cc 
 * Test definitions for the class Hsm.
 *
 * (C) Copyright 2003-2006 by Semih Cemiloglu
 * All rights reserved, see COPYRIGHT file for details.
 *
 * $Id: Hsm_t.cc 116 2006-06-19 06:09:53Z semihc $
 *
 *
 */


#include "OpHsm.hpp"
#include "Hsm.hh"
#include <iostream>
#include <sstream>
#include "TestHarness.hh"

using namespace std;
using namespace TC;
using namespace TC::test;

TC_DEF_FILE("$Revision: 116 $");

namespace {

  struct ParentA 
  { int a; };
  
  struct ParentB
  {
    int b;
    virtual ~ParentB() {}
  };

  struct ParentC : public ParentB
  {
    int c;
  };


// Multiple inheritance is on by default
#ifndef TC_TEST_MI
# define TC_TEST_MI 1
#endif

#if TC_TEST_MI
// We need following derivations to test Multiple Inheritance case
# define TC_TEST_SUPERCLASSES  , public ParentA, public ParentC
#else
# define TC_TEST_SUPERCLASSES
#endif


  enum HsmTstSignals {
    A_SIG = HSM_USER_SIG, B_SIG, C_SIG, 
    D_SIG, E_SIG, F_SIG, G_SIG, H_SIG
  };
  


  /**
   * Optimum HSM test class.
   */
  class OpHsmTst : public OpHsm<OpHsmTst>  TC_TEST_SUPERCLASSES
  {
  public:
    OpHsmTst(ostream& os) 
      : OpHsm<OpHsmTst>(&OpHsmTst::initial), m_os(os), m_cache(false) {}

    void setCacheUse(bool val)
    { m_cache = val; }

    void setFoo(int val)
    { m_foo = val; }

  protected:
   State_t initial(SmEvent const *e);              // initial pseudostate
   State_t s0(SmEvent const *e);                       // state-handler
      State_t s1(SmEvent const *e);                    // state-handler
         State_t s11(SmEvent const *e);                // state-handler 
      State_t s2(SmEvent const *e);                    // state-handler
         State_t s21(SmEvent const *e);                // state-handler
            State_t s211(SmEvent const *e);            // state-handler
  
  private:                               
    // extended state variables...
    int m_foo;
    ostream& m_os;
    bool m_cache;
};


OpHsmTst::State_t OpHsmTst::initial(SmEvent const *) {
  m_os << "top-INIT;"; 
  m_foo = 0;                   // initialize extended state variable
  init(&OpHsmTst::s0);                        // initial transition
  return 0; //SSC: Check later for correctness
}

OpHsmTst::State_t OpHsmTst::s0(SmEvent const *e) {
  switch (e->getSignal()) {
  case HSM_ENTRY_SIG: m_os << "s0-ENTRY;"; return 0;
  case HSM_EXIT_SIG:  m_os << "s0-EXIT;";  return 0;
  case HSM_INIT_SIG:  m_os << "s0-INIT;";  init(&OpHsmTst::s1);   
    return 0;
  case E_SIG: m_os << "s0-E;"; tran(&OpHsmTst::s211, m_cache); return 0;
  }

   return &OpHsmTst::top;
}

OpHsmTst::State_t OpHsmTst::s1(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s1-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s1-EXIT;";  return 0;
   case HSM_INIT_SIG:  m_os << "s1-INIT;";  init(&OpHsmTst::s11);
      return 0;
   case A_SIG: m_os << "s1-A;"; tran(&OpHsmTst::s1, m_cache);   return 0;
   case B_SIG: m_os << "s1-B;"; tran(&OpHsmTst::s11, m_cache);  return 0;
   case C_SIG: m_os << "s1-C;"; tran(&OpHsmTst::s2, m_cache);   return 0;
   case D_SIG: m_os << "s1-D;"; tran(&OpHsmTst::s0, m_cache);   return 0;
   case F_SIG: m_os << "s1-F;"; tran(&OpHsmTst::s211, m_cache); return 0;
   } 
   return &OpHsmTst::s0;
}

OpHsmTst::State_t OpHsmTst::s11(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s11-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s11-EXIT;";  return 0;
   case G_SIG: m_os << "s11-G;"; tran(&OpHsmTst::s211, m_cache); return 0;
   case H_SIG:                    // internal transition with a guard
      if (m_foo) {                        // test the guard condition
         m_os << "s11-H;";
         m_foo = 0;
         return 0;
      }
      break;
   } 
   return &OpHsmTst::s1;
}

OpHsmTst::State_t OpHsmTst::s2(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s2-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s2-EXIT;";  return 0;
   case HSM_INIT_SIG:  m_os << "s2-INIT;";  init(&OpHsmTst::s21); 
      return 0;
   case C_SIG: m_os << "s2-C;"; tran(&OpHsmTst::s1, m_cache);  return 0;
   case F_SIG: m_os << "s2-F;"; tran(&OpHsmTst::s11, m_cache); return 0;
   } 
   return &OpHsmTst::s0;
}

OpHsmTst::State_t OpHsmTst::s21(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s21-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s21-EXIT;";  return 0;
   case HSM_INIT_SIG:  m_os << "s21-INIT;";  init(&OpHsmTst::s211); 
      return 0;
   case B_SIG: m_os << "s21-B;"; tran(&OpHsmTst::s211, m_cache); return 0;
   case H_SIG:                        // self transition with a guard
      if (!m_foo) {                       // test the guard condition
         m_os << "s21-H;";
         m_foo = 1;
         tran(&OpHsmTst::s21, m_cache);   // self transition
         return 0;
      }
      break;                        // break to return the superstate
   } 
   return &OpHsmTst::s2;              // return the superstate
}

OpHsmTst::State_t OpHsmTst::s211(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s211-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s211-EXIT;";  return 0;
   case D_SIG: m_os << "s211-D;"; tran(&OpHsmTst::s21, m_cache); return 0;
   case G_SIG: m_os << "s211-G;"; tran(&OpHsmTst::s0, m_cache);  return 0;
   } 
   return &OpHsmTst::s21;
}


  /**
   * Dynamic HSM Test class
   */
  class DyHsmTst : public Hsm  TC_TEST_SUPERCLASSES
  {
  public:
    DyHsmTst(ostream& os) 
      : Hsm(&DyHsmTst::initial), m_os(os) {}

  protected:
   State_t initial(SmEvent const *e);              // initial pseudostate
   State_t s0(SmEvent const *e);                       // state-handler
      State_t s1(SmEvent const *e);                    // state-handler
         State_t s11(SmEvent const *e);                // state-handler 
      State_t s2(SmEvent const *e);                    // state-handler
         State_t s21(SmEvent const *e);                // state-handler
            State_t s211(SmEvent const *e);            // state-handler
  
  private:                               
    // extended state variables...
    int m_foo;
    ostream& m_os;
  };


DyHsmTst::State_t DyHsmTst::initial(SmEvent const *) {
  m_os << "top-INIT;"; 
  // initialize extended state variable
  m_foo = 0;                   
  // initial transition
  init(&DyHsmTst::s0);                 
  //SSC: Check later for correctness
  return 0; 
}

DyHsmTst::State_t DyHsmTst::s0(SmEvent const *e) {
  switch (e->getSignal()) {
  case HSM_ENTRY_SIG: m_os << "s0-ENTRY;"; return 0;
  case HSM_EXIT_SIG:  m_os << "s0-EXIT;";  return 0;
  case HSM_INIT_SIG:  m_os << "s0-INIT;";  init(&DyHsmTst::s1);   
    return 0;
  case E_SIG: m_os << "s0-E;"; tran(&DyHsmTst::s211); return 0;
  }

   return Action_t(&DyHsmTst::top);
}

DyHsmTst::State_t DyHsmTst::s1(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s1-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s1-EXIT;";  return 0;
   case HSM_INIT_SIG:  m_os << "s1-INIT;";  init(&DyHsmTst::s11);
      return 0;
   case A_SIG: m_os << "s1-A;"; tran(&DyHsmTst::s1);   return 0;
   case B_SIG: m_os << "s1-B;"; tran(&DyHsmTst::s11);  return 0;
   case C_SIG: m_os << "s1-C;"; tran(&DyHsmTst::s2);   return 0;
   case D_SIG: m_os << "s1-D;"; tran(&DyHsmTst::s0);   return 0;
   case F_SIG: m_os << "s1-F;"; tran(&DyHsmTst::s211); return 0;
   } 
   return &DyHsmTst::s0;
}

DyHsmTst::State_t DyHsmTst::s11(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s11-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s11-EXIT;";  return 0;
   case G_SIG: m_os << "s11-G;"; tran(&DyHsmTst::s211); return 0;
   case H_SIG:                    // internal transition with a guard
      if (m_foo) {                        // test the guard condition
         m_os << "s11-H;";
         m_foo = 0;
         return 0;
      }
      break;
   } 
   return &DyHsmTst::s1;
}

DyHsmTst::State_t DyHsmTst::s2(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s2-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s2-EXIT;";  return 0;
   case HSM_INIT_SIG:  m_os << "s2-INIT;";  init(&DyHsmTst::s21); 
      return 0;
   case C_SIG: m_os << "s2-C;"; tran(&DyHsmTst::s1);  return 0;
   case F_SIG: m_os << "s2-F;"; tran(&DyHsmTst::s11); return 0;
   } 
   return &DyHsmTst::s0;
}

DyHsmTst::State_t DyHsmTst::s21(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s21-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s21-EXIT;";  return 0;
   case HSM_INIT_SIG:  m_os << "s21-INIT;";  init(&DyHsmTst::s211); 
      return 0;
   case B_SIG: m_os << "s21-B;"; tran(&DyHsmTst::s211); return 0;
   case H_SIG:                        // self transition with a guard
      if (!m_foo) {                       // test the guard condition
         m_os << "s21-H;";
         m_foo = 1;
         tran(&DyHsmTst::s21);   // self transition
         return 0;
      }
      break;                        // break to return the superstate
   } 
   return &DyHsmTst::s2;              // return the superstate
}

DyHsmTst::State_t DyHsmTst::s211(SmEvent const *e) {
   switch (e->getSignal()) {
   case HSM_ENTRY_SIG: m_os << "s211-ENTRY;"; return 0;
   case HSM_EXIT_SIG:  m_os << "s211-EXIT;";  return 0;
   case D_SIG: m_os << "s211-D;"; tran(&DyHsmTst::s21); return 0;
   case G_SIG: m_os << "s211-G;"; tran(&DyHsmTst::s0);  return 0;
   } 
   return &DyHsmTst::s21;
}




  //! Inspect-Response structure
  struct irst 
  {
    const char* title;
    const char* expect;
    int signal;
  };

  //! Expected responses
  irst irarr[] = {
    {"Sig-A@s11", "s1-A;s11-EXIT;s1-EXIT;s1-ENTRY;s1-INIT;s11-ENTRY;", A_SIG},
    {"Sig-B@s11", "s1-B;s11-EXIT;s11-ENTRY;", B_SIG},
    {"Sig-C@s11", "s1-C;s11-EXIT;s1-EXIT;s2-ENTRY;s2-INIT;s21-ENTRY;s21-INIT;s211-ENTRY;", C_SIG},
    {"Sig-C@s211", "s2-C;s211-EXIT;s21-EXIT;s2-EXIT;s1-ENTRY;s1-INIT;s11-ENTRY;", C_SIG},
    {"Sig-D@s11", "s1-D;s11-EXIT;s1-EXIT;s0-INIT;s1-ENTRY;s1-INIT;s11-ENTRY;", D_SIG},
    {"Sig-E@s11", "s0-E;s11-EXIT;s1-EXIT;s2-ENTRY;s21-ENTRY;s211-ENTRY;", E_SIG},
    {"Sig-F@s211", "s2-F;s211-EXIT;s21-EXIT;s2-EXIT;s1-ENTRY;s11-ENTRY;", F_SIG},
    {"Sig-F@s11", "s1-F;s11-EXIT;s1-EXIT;s2-ENTRY;s21-ENTRY;s211-ENTRY;", F_SIG},
    {"Sig-B@s211", "s21-B;s211-EXIT;s211-ENTRY;", B_SIG},
    {"Sig-D@s211", "s211-D;s211-EXIT;s21-INIT;s211-ENTRY;", D_SIG},
    {"Sig-G@s211", "s211-G;s211-EXIT;s21-EXIT;s2-EXIT;s0-INIT;s1-ENTRY;s1-INIT;s11-ENTRY;", G_SIG},
    {"Sig-H@s11", "", H_SIG},
    {"Sig-G@s11", "s11-G;s11-EXIT;s1-EXIT;s2-ENTRY;s21-ENTRY;s211-ENTRY;", G_SIG},
    {"Sig-H@s211", "s21-H;s211-EXIT;s21-EXIT;s21-ENTRY;s21-INIT;s211-ENTRY;", H_SIG},
    {"Sig-H@s211", ""},
    { 0, 0, 0 }
  };



  class DyRefBase : public Hsm TC_TEST_SUPERCLASSES
  {
  public:
    DyRefBase(ostream& os) 
      : Hsm(&DyRefBase::initial), m_os(os) {}

  protected:
   State_t initial(SmEvent const *e);              // initial pseudostate
   State_t s0(SmEvent const *e);                       // state-handler
      State_t s1(SmEvent const *e);                    // state-handler
      //NOTE: The virtual specifier
      virtual State_t s2(SmEvent const *e);            // state-handler

  
    ostream& m_os;
  };


  class DyRefChild : public DyRefBase
  {
  public:
    DyRefChild(ostream& os) : DyRefBase(os) {}

  protected:
    //NOTE: The virtual specifier
    virtual State_t s2(const SmEvent* e);
  };


  DyRefBase::State_t DyRefBase::initial(SmEvent const *) 
  {
    m_os << "top-INIT;"; 
    init(&DyRefBase::s0);
    return 0;
  }

  DyRefBase::State_t DyRefBase::s0(SmEvent const *e) 
  {
    switch (e->getSignal()) {
    case HSM_ENTRY_SIG: m_os << "s0-ENTRY;"; return 0;
    case HSM_EXIT_SIG:  m_os << "s0-EXIT;";  return 0;
    case HSM_INIT_SIG:  m_os << "s0-INIT;";  init(&DyRefBase::s1);   
      return 0;
    case A_SIG: m_os << "s0-A;"; tran(&DyRefBase::s0); return 0;
    }

    return Action_t(&DyRefBase::top);
  }

  DyRefBase::State_t DyRefBase::s1(SmEvent const *e) 
  {
    switch (e->getSignal()) {
    case HSM_ENTRY_SIG: m_os << "s1-ENTRY;"; return 0;
    case HSM_EXIT_SIG:  m_os << "s1-EXIT;";  return 0;
    case B_SIG: m_os << "s1-B;"; tran(&DyRefBase::s2);  return 0;
    } 
    
    return &DyRefBase::s0;
  }


  DyRefBase::State_t DyRefBase::s2(SmEvent const *e) 
  {
    switch (e->getSignal()) {
    case HSM_ENTRY_SIG: m_os << "s2-ENTRY;"; return 0;
    case HSM_EXIT_SIG:  m_os << "s2-EXIT;";  return 0;
    case B_SIG: m_os << "s2-B{@Base};"; tran(&DyRefBase::s1);  return 0;
    } 
    
    return &DyRefBase::s0;
  }

  DyRefChild::State_t DyRefChild::s2(SmEvent const *e) 
  {
    switch (e->getSignal()) {
    case HSM_ENTRY_SIG: m_os << "s2-ENTRY;"; return 0;
    case HSM_EXIT_SIG:  m_os << "s2-EXIT;";  return 0;
    case B_SIG: m_os << "s2-B{@Child};"; tran(&DyRefChild::s2);  return 0;
    } 
    
    //NOTE: Let the parent class handle the event
    return DyRefBase::s2(e);
  }

  //! Expected responses
  irst refarr[] = {
    {"Sig-B@s1", "s1-B;s1-EXIT;s2-ENTRY;", B_SIG},
    {"Sig-B@s2", "s2-B{@Child};s2-EXIT;s2-ENTRY;", B_SIG},
    {"Sig-A@s2", "s0-A;s2-EXIT;s0-EXIT;s0-ENTRY;s0-INIT;s1-ENTRY;", A_SIG},
    {0, 0, 0}
  };

} // end namespace



namespace TC {
 namespace test {

   template <class H>
   bool HsmExpect(H& hsm,
                  SmEvent* event,
                  ostringstream& os,
                  const char* title,
                  const char* expect)
   {
     TC_LOG_FUNC(HsmExpect);     
     string resp;

     if(event) {
       os.str("");
       hsm.dispatch(event);
     }
     resp = os.str();
     TC_LOG_INFO(title << '=' << resp.c_str());
     if(resp == expect)
       return true;
     else
       return false;
   }


   
                    

   bool OpHsm_test()
   {
     TC_LOG_FUNC(OpHsm_test);
     ostringstream os;
     SmEvent e(A_SIG);
     OpHsmTst hsm(os);
     int i;

     TC_LOG_INFO("Optimum HSM Testing:");
     hsm.setup();
     TC_TEST(HsmExpect(hsm, 0, os, "Init",
                       "top-INIT;s0-ENTRY;s0-INIT;s1-ENTRY;s1-INIT;s11-ENTRY;"));

     TC_LOG_INFO("Signaling without using cache:");
     hsm.setCacheUse(false);
     for(i=0; irarr[i].title; ++i) {
       e.setSignal(irarr[i].signal);
       TC_TEST(HsmExpect(hsm, &e, os, irarr[i].title, irarr[i].expect));
     }

     // Dispatch signal C to start in state s11
     e.setSignal(C_SIG);
     hsm.dispatch(&e);
     hsm.setFoo(0);

     TC_LOG_INFO("Signaling with using cache:");
     hsm.setCacheUse(true);
     for(i=0; irarr[i].title; ++i) {
       e.setSignal(irarr[i].signal);
       TC_TEST(HsmExpect(hsm, &e, os, irarr[i].title, irarr[i].expect));
     }

     return true;
   }

   
   bool DyHsm_test()
   {
     TC_LOG_FUNC(DyHsm_test);
     ostringstream os;
     SmEvent e(A_SIG);
     DyHsmTst hsm(os);


     TC_LOG_INFO("Dynamic HSM Testing:");
     hsm.setup();
     TC_TEST(HsmExpect(hsm, 0, os, "Init",
                       "top-INIT;s0-ENTRY;s0-INIT;s1-ENTRY;s1-INIT;s11-ENTRY;"));

     for(int i=0; irarr[i].title; ++i) {
       e.setSignal(irarr[i].signal);
       TC_TEST(HsmExpect(hsm, &e, os, irarr[i].title, irarr[i].expect));
     }

     return true;
   }


   bool HsmRef_test()
   {
     TC_LOG_FUNC(HsmRef_test);
     ostringstream os;
     SmEvent e(A_SIG);
     DyRefChild hsm(os);


     TC_LOG_INFO("HSM Refinement Testing:");
     hsm.setup();
     TC_TEST(HsmExpect(hsm, 0, os, "Init",
                       "top-INIT;s0-ENTRY;s0-INIT;s1-ENTRY;"));

     for(int i=0; refarr[i].title; ++i) {
       e.setSignal(refarr[i].signal);
       TC_TEST(HsmExpect(hsm, &e, os, refarr[i].title, refarr[i].expect));
     }


     return true;
   }


   bool Hsm_test()
   {
     TC_TEST(OpHsm_test());
     TC_TEST(DyHsm_test());
     TC_TEST(HsmRef_test());
     return true;
   }

 } // end namespace
} // end namespace



